/*
 *  Licensed to GraphHopper GmbH under one or more contributor
 *  license agreements. See the NOTICE file distributed with this work for
 *  additional information regarding copyright ownership.
 *
 *  GraphHopper GmbH licenses this file to you under the Apache License,
 *  Version 2.0 (the "License"); you may not use this file except in
 *  compliance with the License. You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.graphhopper.core.routing.util;

import com.graphhopper.core.util.EdgeIteratorState;
import com.graphhopper.core.util.GHUtility;

import java.util.Arrays;

/**
 * Defines how the graph can be traversed while Dijkstra or similar RoutingAlgorithm is in progress.
 * Different options define how precise turn restrictions and costs are taken into account, but
 * still all are without via-way support. BTW: this would not be done at runtime, this would be a
 * pre-processing step to avoid performance penalties.
 * <p>
 * 定义在Dijkstra或类似的路由算法进行时如何遍历图。不同的选项定义了如何考虑精确的转弯限制和成本，但仍然没有旁路支持。
 * 顺便说一句：这不会在运行时完成，这将是一个预处理步骤，以避免性能损失。
 *
 * @author Peter Karich
 */
public enum TraversalMode {
    /**
     * The simplest traversal mode but without turn restrictions or cost support.
     */
    NODE_BASED(false, 1, false),
    /**
     * Strictly not recommended as it could lead to 'route not found' for bidirectional algorithms.
     * An edged-based traversal mode with basic turn restriction and cost support, including the
     * most scenarios. But without certain turn restrictions and without u-turns. As fast as node
     * based.
     */
    EDGE_BASED_1DIR(true, 1, false),
    /**
     * The bidirectional edged-based traversal mode with turn restriction and cost support. Without
     * u-turn support. 2 times slower than node based.
     */
    EDGE_BASED_2DIR(true, 2, false),
    /**
     * Not recommended as it leads to strange routes that outsmart the turn costs. The most feature
     * rich edged-based traversal mode with turn restriction and cost support, including u-turns. 4
     * times slower than node based.
     */
    EDGE_BASED_2DIR_UTURN(true, 2, true);

    private final boolean edgeBased;
    private final int noOfStates;
    private final boolean uTurnSupport;

    TraversalMode(boolean edgeBased, int noOfStates, boolean uTurnSupport) {
        this.edgeBased = edgeBased;
        this.noOfStates = noOfStates;
        this.uTurnSupport = uTurnSupport;

        if (noOfStates != 1 && noOfStates != 2)
            throw new IllegalArgumentException("Currently only 1 or 2 states allowed");
    }

    public static TraversalMode fromString(String name) {
        try {
            return valueOf(name.toUpperCase());
        } catch (Exception ex) {
            throw new IllegalArgumentException("TraversalMode " + name + " not supported. " + "Supported are: " + Arrays.asList(TraversalMode.values()));
        }
    }

    /**
     * Returns the identifier to access the map of the shortest path tree according to the traversal
     * mode. E.g. returning the adjacent node id in node-based behavior whilst returning the edge id
     * in edge-based behavior
     * <p>
     *
     * @param iterState the current {@link EdgeIteratorState}
     * @param reverse   <code>true</code>, if traversal in backward direction. Will be true only for
     *                  backward searches in bidirectional algorithms.
     * @return the identifier to access the shortest path tree
     */
    public final int createTraversalId(EdgeIteratorState iterState, boolean reverse) {
        if (edgeBased) {
            if (noOfStates == 1) return iterState.getEdge();

            return GHUtility.createEdgeKey(iterState.getBaseNode(), iterState.getAdjNode(), iterState.getEdge(), reverse);
        }

        return iterState.getAdjNode();
    }

    /**
     * If you have an EdgeIteratorState the other createTraversalId is preferred!
     */
    public final int createTraversalId(int baseNode, int adjNode, int edgeId, boolean reverse) {
        if (edgeBased) {
            if (noOfStates == 1) return edgeId;

            return GHUtility.createEdgeKey(baseNode, adjNode, edgeId, reverse);
        }

        return adjNode;
    }

    public int reverseEdgeKey(int edgeKey) {
        if (edgeBased && noOfStates > 1) return GHUtility.reverseEdgeKey(edgeKey);
        return edgeKey;
    }

    public int getNoOfStates() {
        return noOfStates;
    }

    public boolean isEdgeBased() {
        return edgeBased;
    }

    public final boolean hasUTurnSupport() {
        return uTurnSupport;
    }
}
